package org.tlang.ast.list.expr;

import org.tlang.ast.AST;
import org.tlang.ast.ASTLeaf;
import org.tlang.ast.ASTList;
import org.tlang.context.TypeTable;
import org.tlang.context.ValueTable;
import org.tlang.exception.EvalException;
import org.tlang.exception.TypeException;
import org.tlang.type.BoolType;
import org.tlang.type.FloatType;
import org.tlang.type.IntType;
import org.tlang.type.Type;

import java.util.List;

/**
 * 一元运算表达式
 */
public class UnaryExpr extends ASTList {
    public UnaryExpr(List<AST> children) {
        super(children);
    }

    private String operator() {
        return ((ASTLeaf) child(0)).token().getText();
    }

    private AST operand() {
        return child(1);
    }

    @Override
    public String toString() {
        return "-" + operand();
    }

    @Override
    public Object eval(ValueTable valueTable) {
        Object result = operand().eval(valueTable);
        if ("-".equals(operator())) {
            if (result instanceof Long) {
                return -(Long) result;
            } else if (result instanceof Double) {
                return -(Double) result;
            }
        } else if ("!".equals(operator()) && result instanceof Boolean) {
            return !(Boolean) result;
        }

        throw new EvalException("bad type for " + operator(), this);
    }

    @Override
    public Type typeCheck(TypeTable typeTable) throws TypeException {
        String op = operator();
        Type type = operand().typeCheck(typeTable);
        switch (op) {
            case "-":
                if (!(type instanceof IntType) && !(type instanceof FloatType)) {
                    throw new TypeException("bad type " + type, this);
                }
                break;
            case "!":
                type.assertSubTypeOf(BoolType.BOOL_TYPE, typeTable, this);
                break;
            default:
                throw new TypeException("not support op: " + op, this);
        }

        return type;
    }
}
